﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net;

namespace Entangle
{
    class MessageManager
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(MessageManager));

        #region Singleton
        private static MessageManager _instance;
        private MessageManager() {
            if (Configuration.LastPartnerContact == null)
                newestMessageDT = DateTime.Now.AddYears(-1);
            else
                newestMessageDT = Configuration.LastPartnerContact;
        }
        ~MessageManager()
        {
            log.Info("Disposing MessageManager, saving last partner contact (if had).");
            if (hadContanct)
                Configuration.LastPartnerContact = newestMessageDT;
        }
        public static MessageManager Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new MessageManager();
                return _instance;
            }
        }
        #endregion

        private DateTime newestMessageDT;
        private bool hadContanct = false;

        public Message ProcessMessage(Message m)
        {
            if (!hadContanct)
                hadContanct = true;

            if (m.ContentCreationDate < newestMessageDT)
            {
                log.Warn("Message arrived out of order:");
                log.Warn(m.ToString());
                log.Warn("Message will not be procesed.");
                return new Message("Message arrive out of order.", MessageType.INVALID_DATE);
            }
            else
            {
                newestMessageDT = m.ContentCreationDate;
            }

            // TODO: Process message according to type and content.
            log.InfoFormat("Processing message of type: {0}", m.Type);
            try
            {
                switch (m.Type)
                {
                    case MessageType.STATUS_CHANGED:
                        log.Info("Updating partner status.");
                        Entangled.Instance.UpdateState(m.Payload, m.ContentCreationDate);
                        return new Message("State updated.", MessageType.ACKNOWLEDGED);

                    case MessageType.PARTNER_PUBLIC_KEY:
                        log.Info("Submiting own public key.");
                        return new Message(Cryptography.Instance.GetPublicKeyXML(), MessageType.PUBLIC_KEY_SUBMIT);

                    case MessageType.PARTNER_ADD_NEW_AES_KEY:
                        log.Info("Adding new AES key");
                        Cryptography.Instance.AddNewAESKey(m.Payload);
                        return new Message("Key added.", MessageType.AES_KEY_ADDED);

                    default:
                        log.Warn("Message cannot be handled: unknown type.");
                        return new Message("Unknown message type:" + m.Type, MessageType.UNKNOWN_TYPE);
                }
            }
            catch (Exception e)
            {
                log.Error("Error while handling message!", e);
                return new Message("Error occured while handling message. Please notify partner.", MessageType.ERROR);
            }
        }

        public Message SendMessage(Message m)
        {
            // TODO: put message in outgoing queue

            if (!hadContanct)
                hadContanct = true;

            switch (m.Type)
            {
                case MessageType.PARTNER_ADD_NEW_AES_KEY:
                    return new Message("Key 'added'.", MessageType.AES_KEY_ADDED);
                default:
                    return new Message("Message acknowledged", MessageType.ACKNOWLEDGED);
            }
        }
    }
}
